home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
CU Amiga Super CD-ROM 11
/
CU Amiga Magazine's Super CD-ROM 11 (1997)(EMAP Images)(GB)(Track 1 of 3)[!][issue 1997-06].iso
/
cucd
/
programming
/
oberonv4
/
source
/
system
/
ftp.mod
(
.txt
)
< prev
next >
Wrap
Oberon Text
|
1996-07-28
|
19KB
|
471 lines
Syntax10.Scn.Fnt
MODULE FTP; (* is 13.04.94 *)
(* ARD, Sun, 24-Jul-1994 *)
(* ARD, 22.12.94, Streams *)
(* BD, 13.2.96, new NetSystem interface *)
IMPORT Texts, Oberon, Files, Viewers, MenuViewers, TextFrames, NS := NetSystem, Input;
CONST BufSize = 4100;
ControlPort = 21; DataPort = 20;
CR = 0DX; LF = 0AX;
connect = 0; user = 1; pass = 2; command = 3; reply = 4; data = 5; end = 6; more = 7;
cwd = 20; quit = 21; retr = 22; abor = 23; pwd = 24; list = 25; help = 26;
noop = 27; type = 28; nlst = 29; cdup = 30; stor = 31; dele = 32; mkd = 33; rmd = 34; nocmd = 35;
TYPE FTPStream = POINTER TO FTPStreamDesc;
FTPStreamDesc = RECORD
c: NS.Connection;
R: Files.Rider
END;
Task = POINTER TO TaskDesc;
TaskDesc = RECORD (Oberon.TaskDesc)
Stream: FTPStream
END;
VAR W: Texts.Writer;
f: Files.File;
S: Texts.Reader;
T: Texts.Text;
V: Viewers.Viewer;
Control, Data: Task;
lastch, ch: CHAR;
X, Y: INTEGER;
pathname, name, User, Passwd: ARRAY 64 OF CHAR;
buf, bufD: ARRAY BufSize OF CHAR;
TypePar: ARRAY 3 OF CHAR;
state, Cmd, Port, wait: INTEGER;
last, len, lenD, lenF, tot, Length, OldPerc: LONGINT;
WriteToFile, SetPort, SetMode, LineF, first, DosFile, RetCmd, FullDir: BOOLEAN;
PROCEDURE Log(s: ARRAY OF CHAR);
BEGIN Texts.WriteString(W, s); Texts.WriteLn(W); Texts.Append(T, W.buf) END Log;
PROCEDURE SendCommand(cmd, arg: ARRAY OF CHAR);
VAR i, j: INTEGER;
BEGIN i := 0;
WHILE cmd[i] # 0X DO buf[i] := cmd[i]; INC(i) END;
IF arg[0] # 0X THEN buf[i] := " "; INC(i); j := 0;
WHILE arg[j] # 0X DO buf[i] := arg[j]; INC(i); INC(j) END
END;
buf[i] := CR; buf[i+1] := LF;
NS.WriteBytes(Control.Stream.c, 0, i+2, buf)
END SendCommand;
PROCEDURE ChangePort(port: INTEGER);
VAR i, j, k: LONGINT; help: ARRAY 10 OF CHAR; adr: ARRAY 32 OF CHAR; x1, x2: INTEGER;
BEGIN
i := 0; k:= 0;
WHILE k < LEN(NS.hostIP) DO
x1:= ORD(NS.hostIP[k]);
j:=0; WHILE x1> 0 DO help[j]:= CHR((x1 MOD 10) + ORD("0")); INC(j); x1:= x1 DIV 10 END; DEC(j);
WHILE j >=0 DO adr[i]:= help[j]; INC(i); DEC(j) END;
adr[i]:= ",";
INC(k); INC(i);
END;
x1 := port DIV 256; x2 := port MOD 256;
j := 0; WHILE x1 > 0 DO help[j] := CHR(x1 MOD 10 + ORD("0")); INC(j); x1 := x1 DIV 10 END; DEC(j);
WHILE j >= 0 DO adr[i] := help[j]; INC(i); DEC(j) END;
j := 0;
IF x2 > 0 THEN
WHILE x2 > 0 DO help[j] := CHR(x2 MOD 10 + ORD("0")); INC(j); x2 := x2 DIV 10 END; DEC(j)
ELSE help[0] := "0" END;
adr[i] := ","; INC(i);
WHILE j >= 0 DO adr[i] := help[j]; INC(i); DEC(j) END;
adr[i] := 0X;
i := 0; WHILE adr[i] # 0X DO IF adr[i] = "." THEN adr[i] := "," END; INC(i) END;
SendCommand("PORT", adr);
END ChangePort;
PROCEDURE ReadPath;
VAR i: INTEGER;
BEGIN
i := 0; pathname[i] := 0X;
WHILE (ch = " ") OR (ch = CR) OR (ch = 9X) DO Texts.Read(S, ch) END;
WHILE (ch # "~") & (ch # " ") & (ch # "/") & (ch # CR) & (ch # 9X) DO pathname[i] := ch; INC(i); Texts.Read(S, ch) END;
IF (ch = "/") THEN Texts.Read(S, ch);
IF (ch = "d") THEN FullDir := TRUE; Texts.Read(S, ch) END
END;
pathname[i] := 0X;
END ReadPath;
PROCEDURE SearchName;
VAR i, j: INTEGER;
BEGIN
i := 0; j := 0;
WHILE (pathname[i] # 0X) DO IF (pathname[i] = "/") THEN j := i+1 END; INC(i) END;
IF TRUE THEN i := 0; WHILE (pathname[j] # 0X) DO name[i] := pathname[j]; INC(i); INC(j) END; name[i] := 0X
ELSE HALT(99) END
END SearchName;
(* Buffer handling ==============================================================================*)
PROCEDURE ResIs(str: ARRAY OF CHAR): BOOLEAN;
BEGIN
RETURN (buf[0] = str[0]) & (buf[1] = str[1]) & (buf[2] = str[2])
END ResIs;
PROCEDURE WriteText;
VAR num: ARRAY 10 OF CHAR; i, j: LONGINT;
BEGIN
IF ResIs("150") & RetCmd THEN i := 0; RetCmd := FALSE;
WHILE (buf[i] # "(" ) DO INC(i) END; j := 0; INC(i);
WHILE (buf[i] # " " ) & (buf[i] # ".") DO num[j] := buf[i]; INC(i); INC(j) END; DEC(j);
IF (buf[i] = ".") THEN
WHILE (buf[i] # "(") DO INC(i) END; j := 0; INC(i);
WHILE (buf[i] # " " ) DO num[j] := buf[i]; INC(i); INC(j) END; DEC(j)
END;
i := 1; Length := 0;
WHILE (j >= 0) DO Length := Length + (ORD(num[j])-48)*i; i := i*10; DEC(j) END;
END;
Texts.WriteString(W, buf); Texts.WriteLn(W);
IF (V = NIL) OR (V.state <= 0) THEN Oberon.AllocateSystemViewer(Oberon.Par.vwr.X, X, Y);
V := MenuViewers.New(TextFrames.NewMenu("FTP", "System.Close"), TextFrames.NewText(T, 0), TextFrames.menuH, X, Y)
END;
Texts.Append(T, W.buf);
END WriteText;
PROCEDURE TransBufW;
VAR i: LONGINT;
BEGIN
IF (TypePar[0] = "I") THEN
i := 0;
WHILE (i < lenD) DO
IF (bufD[i] # LF) THEN Texts.Write(W, bufD[i]) ELSE Texts.WriteLn(W) END;
INC(i)
END
ELSE
i := last;
WHILE (i < lenD) DO
IF (bufD[i] # CR) THEN Texts.Write(W, bufD[i]); INC(i) ELSE Texts.WriteLn(W); INC(i,2) END
END;
IF (i > lenD) THEN last := i-lenD END
END;
Texts.Append(T, W.buf)
END TransBufW;
PROCEDURE TransBufF;
VAR i: INTEGER; Perc: LONGINT;
BEGIN
i := 0;
WHILE (i < lenD) DO
IF (bufD[i] # LF) & (bufD[i] # CR) THEN Files.Write(Data.Stream.R, bufD[i]); lastch := bufD[i]; INC(tot)
ELSIF (bufD[i] = CR) THEN Files.Write(Data.Stream.R, bufD[i]); lastch := bufD[i]; INC(tot)
ELSIF (bufD[i] = LF) & (lastch # CR) THEN Files.Write(Data.Stream.R, CR); lastch := bufD[i]; INC(tot) END;
INC(i)
END;
Perc := ENTIER(tot*100/Length);
IF (Perc MOD 5 = 0) & (Perc # OldPerc) THEN OldPerc := Perc; Texts.WriteInt(W, Perc, 5); Texts.WriteString(W," %");
IF (Perc = 50) THEN Texts.WriteLn(W) END;
Texts.Append(T, W.buf)
END TransBufF;
PROCEDURE SendFile(SendType: CHAR);
VAR x: CHAR; len, Perc: LONGINT;
BEGIN
IF ~Data.Stream.R.eof THEN
IF (SendType = "A") THEN
lenD := 0;
IF LineF THEN bufD[lenD] := LF; INC(lenD); LineF := FALSE END;
Files.Read(Data.Stream.R, x);
WHILE ~Data.Stream.R.eof & (lenD < BufSize) DO
bufD[lenD] := x; INC(lenD);
IF (x = CR) & (lenD < BufSize) THEN bufD[lenD] := LF; INC(lenD)
ELSIF (x = CR) & (lenD = BufSize) THEN LineF := TRUE END;
Files.Read(Data.Stream.R, x);
END;
NS.WriteBytes(Data.Stream.c, 0, lenD, bufD);
ELSE (* Image File *)
len := lenF - Files.Pos(Data.Stream.R);
IF (len <= BufSize) THEN lenD := SHORT(len) ELSE lenD := BufSize END;
Files.ReadBytes(Data.Stream.R, bufD, lenD);
NS.WriteBytes(Data.Stream.c, 0, lenD, bufD);
IF (len <= BufSize) THEN Files.Read(Data.Stream.R, x) END;
END;
Perc := ENTIER(100*Files.Pos(Data.Stream.R)/lenF);
IF (Perc MOD 5 = 0) & (Perc # OldPerc) THEN OldPerc := Perc; Texts.WriteInt(W, Perc, 5);
Texts.WriteString(W," %");
IF (Perc = 50) THEN Texts.WriteLn(W) END;
Texts.Append(T, W.buf)
END
ELSE
IF ((SendType = "I") OR (SendType = "A")) & ~LineF THEN (* Timeout *)
NS.CloseConnection(Data.Stream.c);
Oberon.Remove(Data); Data := NIL; state := reply; f := NIL;
Log(" file sent");
ReadPath;
IF pathname # "" THEN Cmd := stor END
END
END SendFile;
(* Send & Receive handlers =========================================================================*)
PROCEDURE Receive;
VAR Perc: LONGINT; res: INTEGER;
newC: NS.Connection;
BEGIN
INC(Data.time, Input.TimeUnit DIV 4);
IF NS.Requested(Data.Stream.c) THEN
NS.Accept(Data.Stream.c, newC, res);
NS.CloseConnection(Data.Stream.c);
Data.Stream.c:= newC;
ELSIF (NS.Available(Data.Stream.c) > 0) THEN (* established *)
lenD := NS.Available(Data.Stream.c);
IF lenD > 0 THEN
IF lenD > BufSize THEN lenD := BufSize END;
NS.ReadBytes(Data.Stream.c, 0, lenD, bufD);
IF ~WriteToFile THEN TransBufW
ELSIF (TypePar[0] = "A") THEN TransBufF (* ASCII File *)
ELSE Files.WriteBytes(Data.Stream.R, bufD, lenD); tot := tot+lenD; Perc := ENTIER(100*tot/Length); (* Image File *)
IF (Perc MOD 5 = 0) & (Perc # OldPerc) THEN OldPerc := Perc; Texts.WriteInt(W,Perc,5);
Texts.WriteString(W," %");
IF (Perc = 50) THEN Texts.WriteLn(W) END;
Texts.Append(T, W.buf)
END
END
END;
ELSIF (NS.Available(Control.Stream.c) > 0) & (NS.Available(Data.Stream.c) = 0) THEN
NS.CloseConnection(Data.Stream.c);
Oberon.Remove(Data); Data := NIL; state := reply;
IF WriteToFile THEN Texts.WriteLn(W) END;
Texts.Append(T, W.buf);
IF (f # NIL) THEN Files.Register(f); f := NIL; tot := 0 END;
ReadPath;
IF pathname # "" THEN Cmd := retr END
END Receive;
PROCEDURE Send;
VAR res: INTEGER; newC: NS.Connection;
BEGIN
INC(Data.time, Input.TimeUnit DIV 4);
IF NS.Requested(Data.Stream.c) THEN
NS.Accept(Data.Stream.c, newC, res);
NS.CloseConnection(Data.Stream.c);
Data.Stream.c:= newC;
ELSIF ((NS.State(Data.Stream.c) = NS.inout) OR (NS.State(Data.Stream.c) = NS.out)) THEN SendFile(TypePar[0]) END
(* established *)
END Send;
PROCEDURE Handle;
VAR res: INTEGER;
BEGIN
INC(Control.time, Input.TimeUnit DIV 4);
CASE state OF
connect:
IF NS.Available(Control.Stream.c) >= 3 THEN NS.ReadString(Control.Stream.c, buf); WriteText;
WHILE buf[3] = "-" DO NS.ReadString(Control.Stream.c, buf); WriteText END;
IF ResIs("220") THEN SendCommand("USER", User); state := user
ELSIF ResIs("120") THEN (* wait *)
ELSE SendCommand("QUIT", ""); state := end END
END|
user:
IF NS.Available(Control.Stream.c) >= 3 THEN NS.ReadString(Control.Stream.c, buf); WriteText;
WHILE buf[3] = "-" DO NS.ReadString(Control.Stream.c, buf); WriteText END;
IF ResIs("230") THEN state := command
ELSIF ResIs("331") THEN SendCommand("PASS", Passwd); state := pass
ELSIF ResIs("530") THEN Log("Login refused"); SendCommand("QUIT", ""); state := end
ELSE SendCommand("QUIT", ""); state := end END
END|
pass:
IF NS.Available(Control.Stream.c) >= 3 THEN NS.ReadString(Control.Stream.c, buf); WriteText;
IF (buf[3] = "-") THEN state := more; first := FALSE
ELSIF ResIs("230") THEN state := command
ELSIF ResIs("530") THEN Log("Login refused"); SendCommand("QUIT", ""); state := end
ELSE SendCommand("QUIT", ""); state := end END
END|
command:
CASE Cmd OF
pwd: SendCommand("PWD",""); state := reply; Cmd := nocmd|
type: SendCommand("TYPE", TypePar); state := reply; Cmd := nocmd|
cwd: SendCommand("CWD", pathname); state := reply; Cmd := nocmd|
cdup: SendCommand("CDUP", ""); state := reply; Cmd := nocmd|
mkd: SendCommand("MKD", pathname); state := reply; Cmd := nocmd|
rmd: SendCommand("RMD", pathname); state := reply; Cmd := nocmd|
help: SendCommand("HELP", name); state := reply; Cmd := nocmd|
abor: SendCommand("ABOR", ""); state := reply; Cmd := nocmd|
noop: SendCommand("NOOP", ""); state := reply; Cmd := nocmd|
quit: SendCommand("QUIT", ""); state := end; Cmd := nocmd|
list, nlst:
IF ~SetPort THEN NEW(Data); NEW(Data.Stream);
REPEAT INC(Port);
NS.OpenConnection(Data.Stream.c, Port, NS.anyIP, NS.anyport, res)
UNTIL res = NS.done;
ChangePort(Port); SetPort := TRUE; state := reply
ELSE
Data.handle := Receive;
SetPort := FALSE; last := 0;
IF Cmd = list THEN SendCommand("LIST", pathname) ELSE SendCommand("NLST", pathname) END;
WriteToFile := FALSE; state := reply; Cmd := nocmd;
END|
dele: SendCommand("DELE", pathname); state := reply;
ReadPath;
IF pathname # "" THEN Cmd := dele ELSE Cmd := nocmd END|
retr:
IF ~SetPort THEN NEW(Data); NEW(Data.Stream);
REPEAT INC(Port);
NS.OpenConnection(Data.Stream.c, Port, NS.anyIP, NS.anyport, res)
UNTIL res = NS.done;
ChangePort(Port); SetPort := TRUE; state := reply
ELSIF ~SetMode THEN SendCommand("TYPE", TypePar); SetMode := TRUE; state := reply
ELSE
Data.handle := Receive;
OldPerc := 0; SetPort := FALSE; SetMode := FALSE; last := 0; lastch := 0X; tot := 0;
SendCommand("RETR", pathname); SearchName;
(* IF DosFile THEN f := Files.NewDOS(Path) ELSE *)
f := Files.New(name);
(* END; *)
Files.Set(Data.Stream.R, f, 0);
WriteToFile := TRUE; state := reply; Cmd := nocmd; RetCmd := TRUE
END|
stor:
IF ~SetPort THEN NEW(Data); NEW(Data.Stream);
REPEAT INC(Port);
NS.OpenConnection(Data.Stream.c, Port, NS.anyIP, NS.anyport, res)
UNTIL res = NS.done;
ChangePort(Port); SetPort := TRUE; state := reply
ELSIF ~SetMode THEN SendCommand("TYPE", TypePar); SetMode := TRUE; state := reply
ELSE
Data.handle := Send;
OldPerc := 0; SetPort := FALSE; SetMode := FALSE; LineF := FALSE;
SearchName; f := Files.Old(pathname);
IF (f # NIL) THEN
SendCommand("STOR", pathname);
lenF := Files.Length(f); Files.Set(Data.Stream.R, f, 0);
state := reply; Cmd := nocmd
ELSE Log(" file not found"); NS.CloseConnection(Data.Stream.c); state := command; Cmd := nocmd END
END|
ELSE END|
reply:
IF NS.Available(Control.Stream.c) >= 3 THEN len := NS.Available(Control.Stream.c); NS.ReadString(Control.Stream.c, buf);
wait := 1000;
WriteText;
IF (buf[3] = "-") THEN state := more; first := FALSE
ELSIF ResIs("257") OR ResIs("226") OR ResIs("250") OR ResIs("200") OR ResIs("225") THEN state := command
ELSIF ResIs("150") THEN state := data; Oberon.Install(Data)
ELSIF ResIs("421") THEN SendCommand("QUIT", ""); state := end;
ELSE state := command;
IF (Data # NIL) & (Data.Stream.c # NIL) THEN NS.CloseConnection(Data.Stream.c); Data := NIL END
END
END|
more:
IF NS.Available(Control.Stream.c) >= 3 THEN
NS.ReadString(Control.Stream.c, buf); wait := 1000; first := TRUE; WriteText; state := more
ELSIF first & ResIs("530") THEN SendCommand("QUIT", ""); state := end
ELSIF first THEN DEC(wait); IF (wait = 0) THEN state := command END END|
data: IF (Cmd = abor) THEN SendCommand("ABOR", ""); state := reply; Cmd := nocmd END|
end:
REPEAT UNTIL (NS.Available(Control.Stream.c) > 0);
NS.ReadString(Control.Stream.c, buf); WriteText;
NS.CloseConnection(Control.Stream.c);
Log(" FTP Stopped"); Oberon.Remove(Control); Control := NIL|
END
END Handle;
(* Command procedures =======================================================================*)
PROCEDURE Connect*;
VAR S: Texts.Scanner; res: INTEGER;
remoteIP: NS.IPAdr;
BEGIN
IF Control = NIL THEN
Texts.OpenScanner(S, Oberon.Par.text, Oberon.Par.pos); f := NIL;
Texts.Scan(S);
IF (S.class = Texts.Name) OR (S.class = Texts.String) THEN
SetPort := FALSE; SetMode := FALSE; TypePar := "A ";
state := connect;
NEW(Control); NEW(Control.Stream);
Log(" FTP (ARD/IS, 9. 1. 95) ");
Log(" trying to open connection...");
NS.GetIP(S.s, remoteIP);
NS.OpenConnection(Control.Stream.c, NS.anyport, remoteIP, ControlPort, res);
COPY(NS.user, User); COPY(NS.passwd, Passwd);
IF res = NS.done THEN
Texts.Scan(S);
IF (S.class = Texts.Name) THEN COPY(S.s, User); Texts.Scan(S); Texts.Scan(S);
IF (S.class = Texts.Name) THEN COPY(S.s, Passwd) END
END;
Control.handle := Handle; Oberon.Install(Control)
ELSIF res = NS.timeout THEN Log("Connect timed out")
ELSE Log("Not Done") END
ELSE Log("Invalid name") END
ELSE Log("Already connected") END
END Connect;
PROCEDURE Start*;
BEGIN
NS.Start;
END Start;
PROCEDURE Stop*; (* only in desperate case *)
BEGIN
IF (Data # NIL) THEN
IF (Data.Stream.c # NIL) THEN NS.CloseConnection(Data.Stream.c) END;
Oberon.Remove(Data); Data := NIL
END;
IF (Control # NIL) THEN
IF (Control.Stream.c # NIL) THEN NS.CloseConnection(Control.Stream.c) END;
Oberon.Remove(Control); Control := NIL;
END;
Log("FTP abnormally stopped")
END Stop;
PROCEDURE SetCmd(cmd: INTEGER; txt: ARRAY OF CHAR);
BEGIN
IF (state = command) OR (txt = "FTP.Abort") THEN Cmd := cmd; Log(txt) ELSE Log("previous command not accomplished") END
END SetCmd;
PROCEDURE Clear*;
BEGIN SetCmd(nocmd, "FTP.Clear") END Clear;
PROCEDURE CurrentDir*;
BEGIN SetCmd(pwd, "FTP.CurrentDir") END CurrentDir;
PROCEDURE Disconnect*;
BEGIN SetCmd(quit, "FTP.Disconnect") END Disconnect;
PROCEDURE Abort*;
BEGIN SetCmd(abor, "FTP.Abort") END Abort;
PROCEDURE Check*;
BEGIN SetCmd(noop, "FTP.Check") END Check;
PROCEDURE Directory*;
BEGIN
IF state = command THEN Log("FTP.Directory");
Texts.OpenReader(S, Oberon.Par.text, Oberon.Par.pos); Texts.Read(S, ch);
FullDir := FALSE; ReadPath; DosFile := FALSE;
IF FullDir THEN Cmd := list ELSE Cmd := nlst END
ELSE Log("previous command not accomplished") END
END Directory;
PROCEDURE ChangeDir*;
BEGIN
IF state = command THEN Log("FTP.ChangeDir");
Texts.OpenReader(S, Oberon.Par.text, Oberon.Par.pos); Texts.Read(S, ch);
ReadPath;
IF pathname = ".." THEN Cmd := cdup ELSE Cmd := cwd END
ELSE Log("previous command not accomplished") END
END ChangeDir;
PROCEDURE GetArg(cmd: INTEGER; txt: ARRAY OF CHAR);
BEGIN
IF state = command THEN Log(txt);
Texts.OpenReader(S, Oberon.Par.text, Oberon.Par.pos); Texts.Read(S, ch);
ReadPath; Cmd := cmd
ELSE Log("previous command not accomplished") END
END GetArg;
PROCEDURE MakeDir*;
BEGIN GetArg(mkd, "FTP.MakeDir") END MakeDir;
PROCEDURE RemoveDir*;
BEGIN GetArg(rmd, "FTP.RemoveDir") END RemoveDir;
PROCEDURE DeleteFile*;
BEGIN GetArg(dele, "FTP.DeleteFile") END DeleteFile;
PROCEDURE SetType*;
BEGIN GetArg(type, "FTP.SetType"); COPY(pathname, TypePar) END SetType;
PROCEDURE RetrieveFile*;
BEGIN GetArg(retr, "FTP.RetrieveFile"); DosFile := FALSE; TypePar[0] := "I"; TypePar[1] := 0X END RetrieveFile;
PROCEDURE RetrieveText*;
BEGIN GetArg(retr, "FTP.RetrieveText"); DosFile := FALSE; TypePar[0] := "A"; TypePar[1] := 0X END RetrieveText;
PROCEDURE RetrieveDOSFile*;
BEGIN GetArg(retr, "FTP.RetrieveDOSFile"); DosFile := TRUE; TypePar[0] := "I"; TypePar[1] := 0X END RetrieveDOSFile;
PROCEDURE RetrieveDOSText*;
BEGIN GetArg(retr, "FTP.RetrieveDOSText"); DosFile := TRUE; TypePar[0] := "A"; TypePar[1] := 0X END RetrieveDOSText;
PROCEDURE StoreFile*;
BEGIN GetArg(stor, "FTP.StoreFile"); TypePar[0] := "I"; TypePar[1] := 0X END StoreFile;
PROCEDURE StoreText*;
BEGIN GetArg(stor, "FTP.StoreText"); TypePar[0] := "A"; TypePar[1] := 0X END StoreText;
PROCEDURE Help*;
VAR scan: Texts.Scanner; beg, end, time: LONGINT; text: Texts.Text;
BEGIN
IF state = command THEN
Texts.OpenScanner(scan, Oberon.Par.text, Oberon.Par.pos);
Texts.Scan(scan);
IF (scan.class = Texts.Char) & (scan.c = "^") THEN
Oberon.GetSelection(text, beg, end, time);
IF (time >= 0) THEN Texts.OpenScanner(scan, text, beg); Texts.Scan(scan) END;
END;
IF (scan.class = Texts.Name) OR (scan.class = Texts.String) THEN COPY(scan.s, name); Cmd := help END
END Help;
PROCEDURE ClearLog*;
VAR F: TextFrames.Frame;
BEGIN
IF (V.dsc # NIL) & (V.dsc.next IS TextFrames.Frame) THEN
F := V.dsc.next(TextFrames.Frame); Texts.Delete(F.text, 0, F.text.len)
END;
END ClearLog;
BEGIN
Control := NIL; Data := NIL;
Port := 1499; RetCmd := FALSE;
T := TextFrames.Text(""); Texts.OpenWriter(W);
Oberon.AllocateSystemViewer(Oberon.Mouse.X, X, Y);
V := MenuViewers.New(TextFrames.NewMenu("FTP", "System.Close FTP.ClearLog"), TextFrames.NewText(T, 0), TextFrames.menuH, X, Y);
END FTP.